#!/bin/sh

# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Check uber shopfloor server, fetch and parse board config.
#
# Channel image URLs and checksums are sent out through stdout for shell
# to eval. The format:
#
#   SRC_{CHANNEL}_URL=http://shopfloor_server:port/pathto/gzimage
#   SRC_{CHANNEL}_CHECKSUM=plain_image_binarysha1_base64
#
# For memento updater compatibility, images should be generated by script
# make_factory_package.sh.
#
# Example:
#   SHOPFLOOR="http://SF_SERVER:SF_PORT"
#   BOARD="testboard"
#   result=$(ping_shopfloor)
#   rc="$?"
#   if [ "$rc" = "0" ]; then
#     eval "$result"
#   fi
#   if [ "$SHOPFLOOR_INSTALL" = "1" ]; then
#     do_install
#     ...
#

sf_log() {
  echo "$(date) $*" >&2
}

ping_shopfloor() {
  local channel_hash=""
  local channel_file=""
  local channel_name=""
  local config_file="default.conf"
  local line=""
  local resource_url=""
  local wget_output=""
  local wget_rc="0"
  local channels=""
  local cleanup_files=""

  # Get config file name from BOARD
  if [ -n "$BOARD" ]; then
    config_file="${BOARD}.conf"
  fi

  # Get resource URL from SHOPFLOOR or OMAHA
  if [ -n "$SHOPFLOOR" ]; then
    resource_url="$SHOPFLOOR/res"
  elif [ -n "$OMAHA" ]; then
    resource_url="${OMAHA%/update}/res"
  else
    sf_log "ping_shopfloor: SHOPFLOOR and OMAHA not found."
    return 1
  fi

  # Try to get resource map from Umpire. ETH_INTERFACE is passed from
  # factory_install.sh.
  local sn="$(vpd -i RO_VPD -g serial_number)" || sn=""
  local mlb_sn="$(vpd -i RO_VPD -g mlb_serial_number)" || mlb_sn=""
  local board="${BOARD}"
  local mac_addr="$(ip link show ${ETH_INTERFACE} | grep link/ether |
      tr -s ' '| cut -d ' ' -f 3)"
  local resourcemap_rc="0"
  local resourcemap=""
  local mac="mac.${ETH_INTERFACE}=${mac_addr};"
  local values="sn=${sn}; mlb_sn=${mlb_sn}; board=${board}; ${mac}"
  local empty_values="firmware=; ec=; stage=;"
  local x_umpire_dut_header="X-Umpire-DUT: ${values} ${empty_values}"
  local target="${resource_url%/res}/resourcemap"
  sf_log "ping_shopfloor: ${x_umpire_dut_header}"

  resourcemap="$(wget -O - --header "${x_umpire_dut_header}" "${target}")" ||
      resourcemap_rc="$?"

  if [ "$resourcemap_rc" != "0" ]; then
    sf_log "ping_shopfloor: could not fetch resourcemap."
  else
    sf_log "ping_shopfloor: got resourcemap: ${resourcemap}"
    config_file="$(echo "${resourcemap}" | grep download_conf |
        cut -d ':' -f 2 | tr -d ' ')"
    sf_log "ping_shopfloor: config_file: ${config_file}"
  fi

  # Ping shopfloor by fetching the config file
  wget_output="$(mktemp "/tmp/conf_XXXXXXXX")"
  cleanup_files="$wget_output"
  wget -O $wget_output "$resource_url/$config_file" 2>/dev/null ||
    wget_rc="$?"

  if [ "$wget_rc" != "0" ]; then
    sf_log "ping_shopfloor: could not fetch $resource_url/$config_file"
    [ -f "$cleanup_files" ] && rm $cleanup_files
    return 1
  fi

  # Fix shell read line without \n
  echo >>"$wget_output"

  # Parse channels and skip comments in a subprocess
  channels="$(mktemp "/tmp/sf_XXXXXXXX")"
  cleanup_files="$cleanup_files $channels"
  while IFS= read line; do
    # Remove leading whitespaces
    line=$(echo $line | sed -e 's/^[ \t]*//')
    # Skip comment and empty lines
    [ -n "$(echo $line | grep -E '^#')" -o -z "$line" ] && continue
    # Split channel, file, and hash
    channel_name=$(echo $line | cut -d':' -f1)
    channel_file=$(echo $line | cut -d':' -f2)
    channel_hash=$(echo $line | cut -d':' -f3)
    if [ -z "$channel_name" -o -z "$channel_file" -o -z "$channel_hash" ]; then
      sf_log "ping_shopfloor: not a channel $resource_url/$config_file ($line)"
      [ -n "$cleanup_files" ] && rm $cleanup_files
      return 1
    fi
    echo "SRC_${channel_name}_URL=$resource_url/$channel_file" >> "$channels"
    echo "SRC_${channel_name}_CHECKSUM=$channel_hash" >> "$channels"
  done <"$wget_output"

  if [ "$?" != "0" ]; then
    [ -n "$cleanup_files" ] && rm $cleanup_files
    return 1
  fi

  cat "$channels"
  echo "SHOPFLOOR_INSTALL=1"

  [ -n "$cleanup_files" ] && rm $cleanup_files
  return 0
}

# Bring up network
bringup_network() {
  # Probe USB Ethernet devices.
  local module
  local module_name
  local result=1
  for module in $(find /lib/modules/ -name "*.ko"); do
    module_name="$(basename "${module%.ko}")"
    modprobe ${module_name}
  done

  # Try to bring up network and get an IP address on each Ethernet device.
  for iface in $(ifconfig -a | grep -Eo 'eth[0-9]+'); do
    ifconfig ${iface} up || true
    udhcpc -t 3 -f -q -n -i ${iface} -s /etc/udhcpc.script && result=0
  done
  return ${result}
}

# Try to register into Overlord server if available.
register_to_overlord() {
  local omaha_url="$1"
  local tty_file="$2"
  local log_file="$3"
  local machine_id=""

  local server="${omaha_url#*://}"
  server="${server%%:*}"
  server="${server%% }"
  server="${server## }"
  [ -n "${server}" ] || return 1

  # Build machine ID by MAC addresses.
  local eth="$(netstat -r | grep '^default ')"
  machine_id="$(ip link show ${eth##* } | grep 'link/ether' |
                sed 's/.*ether //;s/ brd .*//' | tr '\n' ',' | sed 's/,$//')"
  [ -n "${machine_id}" ] || machine_id="UnknownMachineId.$(uuidgen 2>/dev/null)"

  local header_tty="$(mktemp)" header_log="$(mktemp)"
  local header='{"name":"register",
                 "params":{"cid":"%s","mid":"%s","format":%s,"mode":4}}\r\n'
  printf "${header}" "Console" "${machine_id}" "1" >"${header_tty}"
  printf "${header}" "DebugLog" "${machine_id}" "0" >"${header_log}"

  # nc@busybox may have problem in receiving data from pipe (due to buffer size
  # and timeout), so we should write the command into a scripting file and let
  # busybox create the pipe internally.
  local tty_piper="$(mktemp)" log_piper="$(mktemp)"
  echo "tail -n +0 -qF ${header_tty} ${tty_file}" >"${tty_piper}"
  echo "tail -n +0 -qF ${header_log} ${log_file}" >"${log_piper}"
  chmod a+rx "${tty_piper}" "${log_piper}"
  local connect_cmd="while true; do busybox nc ${server} 4455 -e %s;
                     sleep 10; done"

  if [ -n "${tty_file}" ]; then
    setsid sh -c "$(printf "${connect_cmd}" ${tty_piper})" &
  fi
  setsid sh -c "$(printf "${connect_cmd}" ${log_piper})" &
}
